/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Traffic;

//Vehicle Controller has the logic for moving vehicles around as well as a reference to each vehicle

import java.util.ArrayList;
import java.util.Random;

public class VehicleController {

    //A reference to the zones object in the model
    Zones zones;
    //The vehicles
    ArrayList<Vehicle> vehicles;
    //Spawn points for new vehicles;
    ArrayList<SpawnPoint> spawns;
    //Random number generator
    Random random;
    //Vehicles going less then the maximum speed will not move every cycle,
    //Arrays of boolean values determine if a vehicle may move on a given cycle
    private static final boolean[] THIRTY_FIVE  = new boolean[] {false, false, true, false, true, false, true, false, false, true, false, true, false, false, true, false, true};
    private static final boolean[] FORTY = new boolean[]        {false, true, false, true, false, true, false, true, false, true, false, true, false, false, true, false, true};
    private static final boolean[] FORTY_FIVE = new boolean[]   {false, true, false, true, false, true, false, true, false, true, false, true, false, true, true, false, true};
    private static final boolean[] FIFTY = new boolean[]        {false, true, false, true, true, false, true, false, true, true, false, true, false, true, true, false, true};
    private static final boolean[] FIFTY_FIVE = new boolean[]   {true, true, false, true, true, false, true, true, false, true, true, false, true, true, false, true, false};
    private static final boolean[] SIXTY = new boolean[]        {true, true, false, true, true, true, false, true, true, false, true, true, false, true, true, false, true};
    private static final boolean[] SIXTY_FIVE = new boolean[]   {true, true, false, true, true, true, false, true, true, true, false, true, true, true, true, false, true};
    private static final boolean[] SEVENTY = new boolean[]      {true, true, true, false, true, true, true, true, false, true, true, true, true, true, false, true, true};
    private static final boolean[] SEVENTY_FIVE = new boolean[] {true, true, true, false, true, true, true, true, false, true, true, true, true, true, true, true, true};
    private static final boolean[] EIGHTY = new boolean[]       {true, true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true};
    private static final boolean[] EIGHTY_FIVE = new boolean[]  {true};

    //Constructor: Initialises the VehicleController with a reference to the Zones object
    VehicleController(Zones z)
    {
        zones = z;
        vehicles = new ArrayList();
        spawns = new ArrayList();
        random = new Random();
    }

    public void addVehicle(Vehicle v)
    {
        vehicles.add(v);
        zones.addZone(new Zone(v.position, ZoneType.BLOCKAGE));
    }

    //Adds a new Vehicle Spawn Point
    public void addSpawn(SpawnPoint sp)
    {
        spawns.add(sp);
    }

    //Spawns vehicles wherever possible
    private void spawnVehicles()
    {
        for (SpawnPoint sp:spawns)
        {
            //If the spawn point is unblocked, we can spawn a new vehicle
            if(!zones.isBlocked(sp.area))
            {
                //The chance of a new vehicle being spawned
                //Lane width is factored in to compensate for a vehicle taking up multiple squares
                int newChance = random.nextInt(100 * Settings.LANE_WIDTH);
                boolean canSpawn = false;
                //The lanes direction determines the chance of a vehicle spawn
                switch (sp.dir)
                {
                    case NORTH:
                        if (newChance <= Settings.northChance)
                            canSpawn = true;
                        break;
                    case SOUTH:
                        if (newChance <= Settings.southChance)
                            canSpawn = true;
                        break;
                    case EAST:
                        if (newChance <= Settings.eastChance)
                            canSpawn = true;
                        break;
                    case WEST:
                        if (newChance <= Settings.westChance)
                            canSpawn = true;
                        break;
                }

                //If we can spawn a vehicle this cycle
                if (canSpawn)
                {
                    //The new vehicle's length
                    int vehicleLength = random.nextInt(Settings.VEHICLE_MAX_LENGTH - Settings.VEHICLE_MIN_LENGTH) + Settings.VEHICLE_MIN_LENGTH;
                    //The new vehicle's position is based off of the spawn point's
                    PositionArea newVehPos = new PositionArea(sp.area);

                    //Offset the vehicle's position to make it appear off screen
                    //and set its length
                    switch (sp.dir)
                    {
                        case NORTH:
                            newVehPos.y += Settings.VEHICLE_OVERTAKE_DISTANCE;
                            newVehPos.yLength = vehicleLength;
                            break;
                        case SOUTH:
                            newVehPos.y += (Settings.VEHICLE_MAX_LENGTH - vehicleLength);
                            newVehPos.yLength = vehicleLength;
                            break;
                        case EAST:
                            newVehPos.x += (Settings.VEHICLE_MAX_LENGTH - vehicleLength);
                            newVehPos.xLength = vehicleLength;
                            break;
                        case WEST:
                            newVehPos.x += Settings.VEHICLE_OVERTAKE_DISTANCE;
                            newVehPos.xLength = vehicleLength;
                            break;
                    }

                    //Calculate Driver Type
                    int driverType = random.nextInt(Settings.fast + Settings.slow + Settings.normal);
                    int vehicleSpeed = sp.speed;

                    //If the driver is a slow driver
                    if (driverType <= Settings.slow)
                    {
                        //If moves 5km/hr slower then the limit
                        vehicleSpeed -= 5;
                    }
                    //If the driver is a fast driver
                    else if (driverType <= Settings.slow + Settings.fast)
                    {
                        //It moves 5km/hr over the limit
                        vehicleSpeed += 5;
                    }

                    //Calculate if the vehicle is turning or not
                    int turnChance = random.nextInt(100);
                    VehicleTurn newVT = VehicleTurn.NO;
                    if (turnChance <= Settings.leftTurn)
                    {
                        newVT = VehicleTurn.LEFT;
                    }
                    else if (turnChance <= Settings.rightTurn + Settings.leftTurn)
                    {
                        newVT = VehicleTurn.RIGHT;
                    }

                    addVehicle(new Vehicle(newVehPos, sp.dir, vehicleSpeed, newVT));
                }
            }
        }
    }

    //Runs a single cycle
    public void runCycle()
    {
        //Stores if a vehicle will be moving this cycle
        boolean willMove = false;
        //The array position to find the willMove value in
        int willMovePos = 0;
        //Vehicles that are out of bounds and need to be deleted
        ArrayList<Vehicle> oldVehs = new ArrayList();

        //For each vehicle
        for (Vehicle v: vehicles)
        {
            //Check if the vehicle is out of bounds
            if (outOfBounds(v))
            {
                //If so add it to the list of vehicles that need to be removed
                //They cannot be immediately removed while we are iterating through the vehicle array.
                oldVehs.add(v);
            }
            //If the vehicle is due to move this cycle and is not obstructed, it can make a move
            else if (dueToMove(v) && (!immediatelyBlocked(v)))
            {
                //Get the type of area the vehicle is in
                ZoneType vehicleArea = zones.getArea(v.position);

                //If the vehicle is in the intersection
                if (vehicleArea == ZoneType.INTERSECTION)
                {
                    //If the Vehicle wants to turn
                    if (v.turning != VehicleTurn.NO)
                    {
                        //Action taken is determined by how long the vehicle has been in the intersection
                        switch(v.movesUntilTurn)
                        {
                            //If the vehicle is just entering the intersection
                            case -1:
                                turningVehicleEnteringIntersection(v);
                                break;
                            //If the vehicle has reached the point it needs to turn
                            case 0:
                                vehicleTurn(v);
                                break;
                            //If the vehicle has not reached the turning point
                            default:
                                v.movesUntilTurn--;
                                
                        }
                    }
                }

                //If the vehicle is not actively changing lanes,
                //we test if it might want to.
                if (v.secondaryDuration <= 0 && vehicleArea != ZoneType.INTERSECTION)
                {
                    //If the vehicle wants to turn at the intersection
                    if (v.turning != VehicleTurn.NO)
                    {
                        //Try to get it in the right lane
                        switch (v.turning)
                        {
                            case LEFT:
                                    tryMoveLeft(v);
                                break;
                            case RIGHT:
                                //If the vehicle is in the turning lane, it does not need to move anymore
                                if (vehicleArea != ZoneType.TURN_LANE)
                                {
                                    //Otherwise it will try to merge right
                                    tryMoveRight(v);
                                }
                                break;
                        }
                    }
                    //If the vehicle does not want to turn at the intersection
                    //it will attempt a normal overtake
                    else if (vehicleArea != ZoneType.INTERSECTION)
                    {
                        //Test for obstructions squares ahead
                        //This lets us know if we are catching up to the vehicle in front.
                        //Make a copy of the vehicle's current position
                        PositionArea p = new PositionArea(v.position);

                        //Make the copy occupy the vehicle's next position
                        switch (v.primaryDirection)
                        {
                            case NORTH:
                                p.y -= Settings.VEHICLE_OVERTAKE_DISTANCE;
                                p.yLength = Settings.VEHICLE_OVERTAKE_DISTANCE;
                                break;
                            case SOUTH:
                                p.y += Settings.VEHICLE_OVERTAKE_DISTANCE + p.yLength;
                                p.yLength = Settings.VEHICLE_OVERTAKE_DISTANCE;
                                break;
                            case EAST:
                                p.x += Settings.VEHICLE_OVERTAKE_DISTANCE + p.xLength;
                                p.xLength = Settings.VEHICLE_OVERTAKE_DISTANCE;
                                break;
                            case WEST:
                                p.x -= Settings.VEHICLE_OVERTAKE_DISTANCE;
                                p.xLength = Settings.VEHICLE_OVERTAKE_DISTANCE;
                                break;
                        }

                        //If there is an upcoming blockage, the vehicle will try to overtake.
                        if (zones.isBlocked(p))
                        {
                            vehicleOvertake(v);
                        }

                        //System.out.println("Debug: Vehicle: " + vehicles.indexOf(v) + " secDir " + v.secondaryDirection + v.secondaryDuration);
                    }
                }

                //Now we can actually move the vehicle
                switch (v.primaryDirection)
                {
                    case NORTH:
                        v.position.y -= 1;
                        break;
                    case SOUTH:
                        v.position.y += 1;
                        break;
                    case EAST:
                        v.position.x += 1;
                        break;
                    case WEST:
                        v.position.x -= 1;
                        break;
                }

                if (v.secondaryDuration > 0 && !isBlockedSideways(v))
                {
                    switch(v.secondaryDirection)
                    {
                        case NORTH:
                            v.position.y -= 1;
                            break;
                        case SOUTH:
                            v.position.y += 1;
                            break;
                        case EAST:
                            v.position.x += 1;
                            break;
                        case WEST:
                            v.position.x -= 1;
                            break;
                    }
                    v.secondaryDuration--;

                    //System.out.println("Debug: new vehicle position" + v.position.x + ',' + v.position.y);
                }
            }
        }
        //Remove old vehicles
        for (Vehicle v:oldVehs)
        {
            vehicles.remove(v);
            zones.removeZone(v.position);
            System.out.println("Debug: Removed Vehicle");
        }
        //Attempt to spawn new vehicles
        spawnVehicles();
    }

    //If a vehicle wants to overtake but doesn't care which direction
    //we use this function
    private void vehicleOvertake(Vehicle v)
    {
        //If we can't move right
        if (!tryMoveRight(v))
        {
            //Then try left
            tryMoveLeft(v);
        }
    }

    //Makes a vehicle attempt to merge right
    private boolean tryMoveRight(Vehicle v)
    {
        boolean success = false;

        //The target is the area we will check for obstructions in
        //Starts off being the vehicle's current area
        PositionArea target = new PositionArea(v.position);

        //The area we need to ensure is free of obstructions stretches 4 squares ahead
        //of the car's front (because if the next lane's traffic is inline, there is no
        //point overtaking there. We also check 3 squares back from the vehicle's rear
        //To prevent the possibility of causing other vehicles to attempt an overtake.

        //The modification to the target depends on the vehicle's current direction
        switch(v.primaryDirection)
        {
            case NORTH:
                target.x += Settings.LANE_WIDTH;
                target.y -= Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                target.yLength += Settings.LANE_WIDTH + Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                break;
            case SOUTH:
                target.x -= Settings.LANE_WIDTH;
                target.y -= Settings.LANE_WIDTH;
                target.yLength += Settings.LANE_WIDTH + Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                break;
            case EAST:
                target.x -= Settings.LANE_WIDTH;
                target.y += Settings.LANE_WIDTH;
                target.xLength += Settings.LANE_WIDTH + Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                break;
            case WEST:
                target.x -= Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                target.y -= Settings.LANE_WIDTH;
                target.xLength += Settings.LANE_WIDTH + Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                break;
        }

        //The overtake is only valid if the area is not blocked and is of an area
        //the vehicle will want to enter (only turning vehicles will enter a turning lane)
        if (!zones.isBlocked(target) && (zones.getArea(target) == ZoneType.ROAD || (v.turning == VehicleTurn.RIGHT && zones.getArea(target) == ZoneType.TURN_LANE)) )
        {
            //The vehicle will need to move sideways until it is entirely one lane over
            v.secondaryDuration = Settings.LANE_WIDTH;

            //The vehicle's secondary direction is dependant on its primary
            switch (v.primaryDirection)
            {
                case NORTH:
                    v.secondaryDirection = Direction.EAST;
                    break;
                case SOUTH:
                    v.secondaryDirection = Direction.WEST;
                    break;
                case EAST:
                    v.secondaryDirection = Direction.SOUTH;
                    break;
                case WEST:
                    v.secondaryDirection = Direction.NORTH;
            }

            success = true;
        }
        //System.out.println("Debug: tryMove right:" + success);
        return success;
    }

    //Makes a vehicle attempt to merge left
    private boolean tryMoveLeft(Vehicle v)
    {
        boolean success = false;

        //The target is the area we will check for obstructions in
        //Starts off being the vehicle's current area
        PositionArea target = new PositionArea(v.position);

        //The area we need to ensure is free of obstructions stretches 4 squares ahead
        //of the car's front (because if the next lane's traffic is inline, there is no
        //point overtaking there. We also check 3 squares back from the vehicle's rear
        //To prevent the possibility of causing other vehicles to attempt an overtake.

        //The modification to the target depends on the vehicle's current direction
        switch(v.primaryDirection)
        {
            case NORTH:
                target.x -= Settings.LANE_WIDTH;
                target.y -= Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                target.yLength += Settings.LANE_WIDTH + Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                break;
            case SOUTH:
                target.x += Settings.LANE_WIDTH;
                target.y -= Settings.LANE_WIDTH;
                target.yLength += Settings.LANE_WIDTH + Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                break;
            case EAST:
                target.x -= Settings.LANE_WIDTH;
                target.y -= Settings.LANE_WIDTH;
                target.xLength += Settings.LANE_WIDTH + Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                break;
            case WEST:
                target.x -= Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                target.y += Settings.LANE_WIDTH;
                target.xLength += Settings.LANE_WIDTH + Settings.VEHICLE_OVERTAKE_DISTANCE + 1;
                break;
        }

        //The overtake is only valid if the area is not blocked and is still a road
        if (!zones.isBlocked(target) && (zones.getArea(target) == ZoneType.ROAD))
        {
            //The vehicle will need to move sideways until it is entirely one lane over
            v.secondaryDuration = Settings.LANE_WIDTH;

            //The vehicle's secondary direction is dependant on its primary
            switch (v.primaryDirection)
            {
                case NORTH:
                    v.secondaryDirection = Direction.WEST;
                    break;
                case SOUTH:
                    v.secondaryDirection = Direction.EAST;
                    break;
                case EAST:
                    v.secondaryDirection = Direction.NORTH;
                    break;
                case WEST:
                    v.secondaryDirection = Direction.SOUTH;
            }

            success = true;
        }

        //System.out.println("Debug: tryMove Left:" + success);
        return success;
    }

    //Checks if the passed vehicle will make a movement this cycle
    private boolean dueToMove(Vehicle v) {

        //The return value
        boolean willMove = false;
        
        //Check if vehicle is currently broken down
        if (v.breakdownDur == 0)
        {
            //Check to see if vehicle will breakdown
            int breakdown = random.nextInt(Settings.BREAKDOWN_CHANCE_DENOMINATOR);

            //If the vehicle will breakdown, apply the breakdown duration
            if (breakdown <= Settings.breakdownChance)
            {
                v.breakdownDur = Settings.breakdownDur;
            }

            //Since the chance of moving is based on speed, we use modulous and the
            //cycle counter to get an answer
            switch (v.speed)
            {
                case 35:
                    willMove = THIRTY_FIVE[Settings.cyclesRun % THIRTY_FIVE.length];
                    break;
                case 40:
                    willMove = FORTY[Settings.cyclesRun % FORTY.length];
                    break;
                case 45:
                    willMove = FORTY_FIVE[Settings.cyclesRun % FORTY_FIVE.length];
                    break;
                case 50:
                    willMove = FIFTY[Settings.cyclesRun % FIFTY.length];
                    break;
                case 55:
                    willMove = FIFTY_FIVE[Settings.cyclesRun % FIFTY_FIVE.length];
                    break;
                case 60:
                    willMove = SIXTY[Settings.cyclesRun % SIXTY.length];
                    break;
                case 65:
                    willMove = SIXTY_FIVE[Settings.cyclesRun % SIXTY_FIVE.length];
                    break;
                case 70:
                    willMove = SEVENTY[Settings.cyclesRun % SEVENTY.length];
                    break;
                case 75:
                    willMove = SEVENTY_FIVE[Settings.cyclesRun % SEVENTY_FIVE.length];
                    break;
                case 80:
                    willMove = EIGHTY[Settings.cyclesRun % EIGHTY.length];
                    break;
                case 85:
                    willMove = EIGHTY_FIVE[Settings.cyclesRun % EIGHTY_FIVE.length];
                    break;
            }
        }
        else
        {
            v.breakdownDur--;
        }
        return willMove;
    }

    //Checks if a vehicle has a blockage immediately in front of it
    private boolean immediatelyBlocked(Vehicle v) {
        //Make a copy of the vehicle's current position
        PositionArea target = new PositionArea(v.position);

        boolean returnValue = false;

        //Check the vehicle's primary direction is free
        switch (v.primaryDirection)
        {
            case NORTH:
                target.y -= 1;
                target.yLength = 1;
                break;
            case SOUTH:
                target.y += target.yLength;
                target.yLength = 1;
                break;
            case EAST:
                target.x += target.xLength;
                target.xLength = 1;
                break;
            case WEST:
                target.x -= 1;
                target.xLength = 1;
                break;
        }

        returnValue = zones.isBlocked(target);
        return returnValue;
    }

    //If the vehicle wants to move diagonally, is it blocked to the side
    private boolean isBlockedSideways(Vehicle v)
    {
            PositionArea target = new PositionArea(v.position);

            switch (v.secondaryDirection)
            {
                case NORTH:
                    target.y -= 1;
                    target.yLength = 1;
                    break;
                case SOUTH:
                    target.y += target.yLength;
                    target.yLength = 1;
                    break;
                case EAST:
                    target.x += target.xLength;
                    target.xLength = 1;
                    break;
                case WEST:
                    target.x -= 1;
                    target.xLength = 1;
                    break;
            }
            boolean returnValue = zones.isBlocked(target);
            return returnValue;
    }



    //Logic for determining if a turning vehicle entering the intersection can turn and if so, how far it must move before being able to actually move.
    private void turningVehicleEnteringIntersection(Vehicle v)
    {

        boolean inCorrectLane = false;

        //If the vehicle wants to turn left at the intersection
        if (v.turning == VehicleTurn.LEFT)
        {
            //System.out.print("Debug: Checking turning left heading ");
            //First we need to test if it made it into the leftmost lane
            switch (v.primaryDirection)
            {
                case NORTH:
                    //If the vehicle is in the correct lane
                    //System.out.println("north and has x position " + v.position.x + "lane length = " + Settings.LANE_LENGTH);
                    if (v.position.x == Settings.LANE_LENGTH)
                    {
                        inCorrectLane = true;
                    }
                    break;
                case SOUTH:
                    //System.out.println("south and has x position " + v.position.x + "lane length = " + (Settings.LANE_LENGTH + (Settings.northLanes + Settings.southLanes) * Settings.LANE_WIDTH));
                    //The +1 for the turning lane not added since the vehicle's x position will be one lane to the west.
                    if (v.position.x == (Settings.LANE_LENGTH + (Settings.northLanes + Settings.southLanes) * Settings.LANE_WIDTH))
                    {
                        inCorrectLane = true;
                    }
                    break;
                case EAST:
                    //System.out.println("east and has y position " + v.position.y +' '+ Settings.LANE_LENGTH);
                    if (v.position.y == Settings.LANE_LENGTH)
                    {
                        inCorrectLane = true;
                    }
                    break;
                case WEST:
                    //System.out.println("west and has y position " + v.position.y + ' ' + (Settings.LANE_LENGTH + (Settings.eastLanes + Settings.westLanes) * Settings.LANE_WIDTH));
                    if (v.position.y == (Settings.LANE_LENGTH + (Settings.eastLanes + Settings.westLanes) * Settings.LANE_WIDTH))
                    {
                        inCorrectLane = true;
                    }
                    break;
            }

            //If our vehicle has made it into the correct lane
            if (inCorrectLane)
            {
                //Set the number of moves it must make before actually turning
                //It is minus 2 since the vehicle will be 3 squares into the
                //intersection prior to this counter decrementing
                v.movesUntilTurn = Settings.LANE_WIDTH - 2;
            }
            //If the vehicle is not in the correct lane
            else
            {
                //It will have to go straight
                v.turning = VehicleTurn.NO;
            }

        }
        //If the vehicle is turning right
        else
        {
            //Test if the vehicle made it into the correct lane
            switch (v.primaryDirection)
            {
                case NORTH:
                case SOUTH:
                    if (v.position.x == Settings.LANE_LENGTH + (Settings.northLanes * Settings.LANE_WIDTH))
                    {
                        inCorrectLane = true;
                    }
                    break;
                case EAST:
                case WEST:
                    if (v.position.y == Settings.LANE_LENGTH + (Settings.eastLanes * Settings.LANE_WIDTH))
                    {
                        inCorrectLane = true;
                    }
                    break;
            }

            //If the vehicle is in the correct lane
            if (inCorrectLane)
            {
                //Set the number of moves it must make before turning
                switch(v.primaryDirection)
                {
                    case NORTH:
                        //We add 2 to the number of lanes that must be crossed
                        //before actually turning to account for the turning lane
                        //and the lane that the vehicle will acually enter.
                        //The minus 2 compensates for the vehicles being 2 squares into
                        //the intersection prior to this code executing
                        v.movesUntilTurn = ((Settings.westLanes + 2) * Settings.LANE_WIDTH) - 2;
                        //Also need to make the vehicle move sideways sligtly to prevent collision with a turner headed in the opposite direction
                        v.secondaryDirection = Direction.EAST;
                        v.secondaryDuration = Settings.LANE_WIDTH;
                        break;
                    case SOUTH:
                        v.movesUntilTurn = (Settings.eastLanes + 2) * Settings.LANE_WIDTH - 2;
                        //Also need to make the vehicle move sideways sligtly to prevent collision with a turner headed in the opposite direction
                        v.secondaryDirection = Direction.WEST;
                        v.secondaryDuration = Settings.LANE_WIDTH;
                        break;
                    case EAST:
                        v.movesUntilTurn = (Settings.northLanes + 2) * Settings.LANE_WIDTH - 2;
                        //Also need to make the vehicle move sideways sligtly to prevent collision with a turner headed in the opposite direction
                        v.secondaryDirection = Direction.SOUTH;
                        v.secondaryDuration = Settings.LANE_WIDTH;
                        break;
                    case WEST:
                        v.movesUntilTurn = (Settings.southLanes + 2) * Settings.LANE_WIDTH - 2;
                        //Also need to make the vehicle move sideways sligtly to prevent collision with a turner headed in the opposite direction
                        v.secondaryDirection = Direction.NORTH;
                        v.secondaryDuration = Settings.LANE_WIDTH;
                        break;
                }
            }
            //If the vehicle is not in the correct lane
            else
            {
                //Set it as being unable to move
                v.turning = VehicleTurn.NO;
            }
        }
    }

    //This vehicle needs to make a turn
    private void vehicleTurn(Vehicle v) 
    {
        switch(v.turning)
        {
            case LEFT:
                switch(v.primaryDirection)
                {
                    case NORTH:
                        v.primaryDirection = Direction.WEST;
                        v.position.xLength = v.position.yLength;
                        v.position.yLength = Settings.LANE_WIDTH;
                        v.position.x -= Settings.LANE_WIDTH;
                        break;
                    case SOUTH:
                        v.primaryDirection = Direction.EAST;
                        v.position.xLength = v.position.yLength;
                        v.position.yLength = Settings.LANE_WIDTH;
                        v.position.x += Settings.LANE_WIDTH;
                        v.position.y += v.position.xLength - Settings.LANE_WIDTH;
                        break;
                    case EAST:
                        v.primaryDirection = Direction.NORTH;
                        v.position.yLength = v.position.xLength;
                        v.position.xLength = Settings.LANE_WIDTH;
                        v.position.x += v.position.yLength - Settings.LANE_WIDTH;
                        v.position.y -= Settings.LANE_WIDTH;
                        break;
                    case WEST:
                        v.primaryDirection = Direction.SOUTH;
                        v.position.yLength = v.position.xLength;
                        v.position.xLength = Settings.LANE_WIDTH;
                        v.position.y += Settings.LANE_WIDTH;
                        break;
                }
                break;
            case RIGHT:
                switch(v.primaryDirection)
                {
                    case NORTH:
                        v.primaryDirection = Direction.EAST;
                        v.position.xLength = v.position.yLength;
                        v.position.yLength = Settings.LANE_WIDTH;
                        v.position.x += (Settings.VEHICLE_MAX_LENGTH - v.position.xLength);
                        break;
                    case SOUTH:
                        v.primaryDirection = Direction.WEST;
                        v.position.xLength = v.position.yLength;
                        v.position.yLength = Settings.LANE_WIDTH;
                        v.position.x -= Settings.LANE_WIDTH;
                        v.position.y += (v.position.xLength - Settings.LANE_WIDTH);
                        break;
                    case EAST:
                        v.primaryDirection = Direction.SOUTH;
                        v.position.yLength = v.position.xLength;
                        v.position.xLength = Settings.LANE_WIDTH;
                        v.position.x += (v.position.yLength - Settings.LANE_WIDTH);
                        v.position.y += (Settings.VEHICLE_MAX_LENGTH - v.position.yLength);
                        break;
                    case WEST:
                        v.primaryDirection = Direction.NORTH;
                        v.position.yLength = v.position.xLength;
                        v.position.xLength = Settings.LANE_WIDTH;
                        v.position.y -= Settings.LANE_WIDTH;
                        break;
                }
                break;
        }
        
        //Set the vehicle to stop testing for turns
        v.turning = VehicleTurn.NO;
    }

    //Tests if a vehicle has left the intersection entirely
    private boolean outOfBounds(Vehicle v)
    {
        boolean returnValue = false;
        //The edge the vehicle needs to pass is dependant on direction
        switch(v.primaryDirection)
        {
            case NORTH:
                if (v.position.y < -Settings.VEHICLE_MAX_LENGTH)
                    returnValue = true;
                break;
            case SOUTH:
                if (v.position.y > (2 * Settings.LANE_LENGTH) + ((Settings.eastLanes + Settings.westLanes + 1) * Settings.LANE_WIDTH))
                    returnValue = true;
                break;
            case WEST:
                if (v.position.x < -Settings.VEHICLE_MAX_LENGTH)
                    returnValue = true;
                break;
            case EAST:
                if (v.position.x > (2 * Settings.LANE_LENGTH) + ((Settings.northLanes + Settings.southLanes + 1) * Settings.LANE_WIDTH))
                    returnValue = true;
                break;
        }
        return returnValue;
    }


}
